String clazzTpl({package}) => """
import 'package:xanno/xanno.dart';
import 'package:flutter/material.dart';
import 'package:$package/generated/l10n.dart';
import 'package:$package/store/app_state.dart';
import 'package:$package/route/main.route.dart';
import 'package:$package/widget/theme_builder.dart';
import 'package:$package/mixin/base_widget_mixin.dart';
import 'package:$package/store/theme/theme_state.dart';
import 'package:observable_builder/observable_builder.dart';

class CounterState {
  int counter = 0;
}

/// 支持缓存的方式 Observable(CounterState(), cacheKey: 'xxxx', decoder: CounterState.fromJson);
/// 不支持缓存的方式 Observable(CounterState());
/// 建议统一在 lib/store/app_state.dart 管理;
final observableCounter = Observable(CounterState());

@GRoute(url: '/', title: 'main')
class MainPage extends BaseStatefulWidget {
  final String title;

  const MainPage({Key? key, required this.title}) : super(key: key);

  @override
  _MainPageState createState() => _MainPageState();
}

class _MainPageState extends BaseState<MainPage> with BaseStatefulMixin {
  @override
  void initState() {
    observableCounter.listen((state) {
      /// listen state
      print('listen \${state.counter}');

      /// 模拟错误，产生错误日志，进入debug页面查看
      /// emulate error, generate error log, enter debug page to view
      Future.error('[\${state.counter}] Custom error',
          StackTrace.fromString('StackTrace error'));
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return ThemeBuilder(
      builder: (context, theme) => Scaffold(
        backgroundColor: theme.body,
        appBar: AppBar(
          brightness: theme.type == ThemeType.light
              ? Brightness.light
              : Brightness.dark,
          backgroundColor: theme.body,
          title: Text(
            widget.title,
            style: TextStyle(
              fontSize: 30,
              fontWeight: FontWeight.bold,
              color: theme.title,
            ),
          ),
          actions: [
            IconButton(
              onPressed: () {
                /// change theme
                AppState.observableTheme.notify((theme) => theme.type =
                    theme.type == ThemeType.light
                        ? ThemeType.dark
                        : ThemeType.light);
              },
              icon: Icon(Icons.wb_incandescent, color: theme.title, size: 30),
            )
          ],
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Align(
              child: ObservableBuilder<CounterState>(
                create: observableCounter,
                memo: (state) => [state.counter],
                builder: (context, state) => Text(
                  '\${S.of(context).xanno} 👍 \${state.counter}',
                  style: TextStyle(
                    fontSize: 30,
                    fontWeight: FontWeight.bold,
                    color: theme.title,
                  ),
                ),
              ),
            ),
            TextButton(
              onPressed: () =>
                  Navigator.pushNamed(context, RouteConstant.AppDebugPage),
              style: ButtonStyle(
                backgroundColor:
                    MaterialStateProperty.all(theme.title.withOpacity(0.2)),
                overlayColor:
                    MaterialStateProperty.all(theme.title.withOpacity(0.2)),
                padding: MaterialStateProperty.all(EdgeInsets.all(10)),
                shape: MaterialStateProperty.all(StadiumBorder()),
              ),
              child: Text(
                S.of(context).desc('debug'),
                style: TextStyle(color: theme.title),
              ),
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            /// counter++
            observableCounter.notify((state) => state.counter++);
          },
          backgroundColor: theme.title,
          child: Icon(Icons.add, color: theme.body),
        ),
      ),
    );
  }
}
""";
